package com.zhl.gateway.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.zhl.gateway.util.JwtHelper;
import io.jsonwebtoken.Claims;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * Token验证的过滤器
 */
@Component
@Slf4j
// 注意：springboot约定过滤器的前缀为配置的name，而后面最好统一都是GatewayFilterFactory
// 1、我们在yml中配置的过滤器名称，如果你的自定义过滤器类名为：XxxGatewayFilterFactory，则名字为Xxx
// 2、而如果你的自定义过滤器类名中不以GatewayFilterFactory结尾，例如只为Xxx，则直接写Xxx即可。
// 但推荐按标准写法：自定义GatewayFilterFactory为：XxxGatewayFilterFactory，
// yml中配置name为Xxx
// 另：如果我们的参数只需要一个，我们可以在yml中配置时直接写成过：滤器名字=参数值即可，例如上面的StripPrefix=2
// 主要有两种类型的过滤器
// GlobalFilter：全局过滤器，对所有的路由均起作用
// GatewayFilter：只对指定的路由起作用
// 自定义GatewayFilter又有两种实现方式，一种是直接 实现GatewayFilter接口，另一种是 继承AbstractGatewayFilterFactory类 ,任意选一种即可
//Gateway过滤器的执行顺序
//全局过滤器与其他2类过滤器相比，永远是最后执行的；它的优先级只对其他全局过滤器起作用
// 当默认过滤器与自定义过滤器的优先级一样时，优先出发默认过滤器，然后才是自定义过滤器；同类型的过滤器，出发顺序与他们在配置文件中声明的顺序一致
// 默认过滤器与自定义过滤器使用同样的order顺序空间，即他们会按照各自的顺序来进行排序
public class TokenCheckGatewayFilterFactory extends AbstractGatewayFilterFactory<TokenCheckGatewayFilterFactory.Config> {

    @Autowired
    private JwtHelper jwtHelper;

    //重写构造函数，指定Config对象为我们自定义的静态内部类Config
    public TokenCheckGatewayFilterFactory() {
        super(Config.class);
        log.info("Loaded GatewayFilterFactory [Authorize]");
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("enabled", "authUrl","ignoreGlobalFilter");
    }

    @Override
    public GatewayFilter apply(Config config) {
        log.info("config.enabled: {}",config.enabled);
        log.info("config.authUrl: {}",config.authUrl);
        log.info("过滤器工厂生效，请求进入");
        //===============================注意=================================
        //下面的内部类写法，是为了指定过滤器的优先级，要优先于全局过滤器，否则
        //容易造成全局过滤器 拦截到指定 局部过滤器的配置内容。
//        return new InnerFilter(config);
        return (exchange, chain) -> {
            //TODO 业务处理
            if (!config.isEnabled()) {
                log.info("不需要认证，继续请求");
                return chain.filter(exchange);
            }
            ServerHttpRequest request = exchange.getRequest();
            URI uri = request.getURI();
            log.info("访问接口URL信息: {}", uri.toString());
            String jwtToken = exchange.getRequest().getHeaders().getFirst("Authorization");
            if(StringUtils.isNotBlank(jwtToken)){
                jwtToken=exchange.getRequest().getHeaders().getFirst("token");
            }
            if(StringUtils.isNotBlank(jwtToken)){
                jwtToken=exchange.getRequest().getQueryParams().getFirst("token");
            }
            if(StringUtils.isNotBlank(jwtToken)){
                HttpCookie cookie=exchange.getRequest().getCookies().getFirst("token");
                if(cookie!=null){
                    jwtToken=cookie.getValue();
                }
            }
//            校验jwtToken的合法性
            if (jwtToken != null) {
                Claims claims=jwtHelper.getClaimByToken(jwtToken);
                if(claims!=null){
                    ServerHttpRequest.Builder builder=exchange.getRequest().mutate();
                    for(Map.Entry<String, Object> entry:claims.entrySet()){
                        builder.header(entry.getKey(),String.valueOf(entry.getValue()));
                    }
                    ServerWebExchange build = exchange.mutate().request(builder.build()).build();
                    log.info("请求校验通过，继续请求具体微服务API");
                    return chain.filter(build);
                }
            }

            //不合法(响应未登录的异常)
//            ServerHttpResponse response = exchange.getResponse();
            //设置headers
//            HttpHeaders httpHeaders = response.getHeaders();
//            httpHeaders.add("Content-Type", "application/json; charset=UTF-8");
//            httpHeaders.add("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0");
            //设置body
            log.warn("没有token信息，请求非法");
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("code", HttpStatus.UNAUTHORIZED.value());
            jsonObject.put("message", "请求非法，不允许直接访问");
//            byte[] data = jsonObject.toJSONString().getBytes(StandardCharsets.UTF_8);
//            response.setStatusCode(HttpStatus.UNAUTHORIZED);
//            DataBuffer buffer = response.bufferFactory().wrap(data);
            // 回写信息
//            return response.writeWith(Mono.just(buffer));
            return generateJSON(exchange, jsonObject);
//            return exchange.getResponse().setComplete();
        };
    }

    private <T>Mono generateJSON(ServerWebExchange exchange, T t){
        ServerHttpResponse response = exchange.getResponse();
        byte[] bits = JSON.toJSONString(t).getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = response.bufferFactory().wrap(bits);
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        //指定编码，否则在浏览器中会中文乱码
        response.getHeaders().add("Content-Type", "text/plain;charset=UTF-8");
        response.getHeaders().add("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0");
        return response.writeWith(Mono.just(buffer));
    }

    /**
     * 自定义的config类，用来设置传入的参数
     * 如果我们想支持在yml文件中给自定义过滤器配置特定参数，我们可以给自定义过滤器指定Config：
     * yml参数化配置
     */
    @Data
    public static class Config {
        //Put the configuration properties for your filter here
//         控制是否开启认证
        private boolean enabled;

        // 白名单
        private String authUrl;

        private boolean ignoreGlobalFilter;
    }

    /**
     * 创建一个内部类，来实现2个接口，指定顺序
     * 这里通过Ordered指定优先级
     */
//    private class InnerFilter implements GatewayFilter, Ordered {
//
//        private Config config;
//
//        InnerFilter(Config config) {
//            this.config = config;
//        }
//
//        @Override
//        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//            /*System.out.println("  pre 自定义过滤器工厂 AAAA  " + this.getClass().getSimpleName());
//            boolean root = true == config.isIgnoreGlobalFilter();
//            if (root) {
//                System.out.println("  is root ");
//            } else {
//                System.out.println("  is no root ");
//            }
//            // 在then方法里的，相当于aop中的后置通知
//            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
//                System.out.println("  post 自定义过滤器工厂 AAAA " + this.getClass().getSimpleName());
//            }));*/
//            log.info("进入innerFilter=====" + config.isIgnoreGlobalFilter());
//            if (config.isIgnoreGlobalFilter() == true) {
//                exchange.getAttributes().put(AttrbuteConstant.ATTRIBUTE_IGNORE_TEST_GLOBAL_FILTER, true);
//            }
//            return chain.filter(exchange);
//        }
//
//        @Override
//        public int getOrder() {
//            return -1000;
//        }
//
//    }

    //这个name方法 用来在yml配置中指定对应的过滤器名称
//    @Override
//    public String name() {
//        return "IgnoreAuthFilter";
//    }


}